home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume22 / nn6.4 / part05 < prev    next >
Encoding:
Internet Message Format  |  1990-06-07  |  54.2 KB

  1. Subject:  v22i040:  NN Newsreader, release 6.4, Part05/21
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 6cc7a4e0 72b69816 29afbd42 7cfb04a3
  5.  
  6. Submitted-by: "Kim F. Storm" <storm@texas.dk>
  7. Posting-number: Volume 22, Issue 40
  8. Archive-name: nn6.4/part05
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then feed it
  12. # into a shell via "sh file" or similar.  To overwrite existing files,
  13. # type "sh file -c".
  14. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  15. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  16. # Contents:  man/nngrab.1 newsrc.c sequence.c
  17. # Wrapped by storm@texas.dk on Sun May  6 18:19:25 1990
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. echo If this archive is complete, you will see the following message:
  20. echo '          "shar: End of archive 5 (of 22)."'
  21. if test -f 'man/nngrab.1' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'man/nngrab.1'\"
  23. else
  24.   echo shar: Extracting \"'man/nngrab.1'\" \(1616 characters\)
  25.   sed "s/^X//" >'man/nngrab.1' <<'END_OF_FILE'
  26. X.TH NNGRAB 1 "Release 6.4"
  27. X.UC 4
  28. X.SH NAME
  29. Xnngrab \- news retrieval by keyword (nn)
  30. X.SH SYNOPSIS
  31. X.B nngrab
  32. X.I keyword
  33. X.SH DESCRIPTION
  34. X.I nngrab
  35. Xinvokes \fInn\fP
  36. Xon all USENET articles whose subject (or keyword) field(s)
  37. Xcontain an instance of \fIkeyword\fP.  \fInngrab\fP is a fast
  38. Xequivalent for:
  39. X.sp 0.5v
  40. X    nn -mxX -s/\fIkeyword\fP all
  41. X.LP
  42. XFor example,
  43. X.sp 0.5v
  44. X    nngrab tesla
  45. X.sp 0.5v
  46. Xwill retrieve items concerning Nikola Tesla.
  47. X.LP
  48. XKeyword case is ignored, and the \fIkeyword\fP can be a regular
  49. Xexpressions (escaped to avoid conflicts with the shell).  For example,
  50. X.sp 0.5v
  51. X    nngrab "n.*tesla"
  52. X.LP
  53. XThe range of search includes all newsgroups on the system,
  54. Xincluding ones which are unsubscribed.
  55. X.SH FILES
  56. X.DT
  57. X.ta \w'$db/subjects'u+3m
  58. X.\"ta 0 16
  59. X$db/subjects    subject database
  60. X.DT
  61. X.SH SEE ALSO
  62. Xnn(1), nnspew(8), egrep(1)
  63. X.SH NOTES
  64. X\fInngrap\fP \fIcan be\fP much faster than the equivalent command
  65. Xshown above, if the tertiary news subject
  66. Xdatabase generated by the \fInnspew\fP(8) daemon exists.  To enable
  67. Xthe faster operation, \fInnspew\fP must be executed regularly by cron.
  68. X.LP
  69. X\fInngrab\fP uses \fIegrep\fP(1) to scan the subject database, so
  70. Xif you are not running \fIfast\fP egrep (GNU-style) this is all for
  71. Xnaught.
  72. X.LP
  73. X\fInngrab\fP will use a subject database generated by \fInnspew\fP
  74. Xindependent of its age.  Thus, if you stop running \fInnspew\fP,
  75. Xremember to remove the subjects file as well.
  76. X.SH BUGS
  77. XUnder version 6.4, search of the "Keywords:" field is not supported.
  78. X.br
  79. XSearch on name is not possible either.
  80. X.SH AUTHOR
  81. XJames A. Woods, NASA Ames Research Center
  82. X.br
  83. XE-mail: jaw@ames.arc.nasa.gov
  84. END_OF_FILE
  85.   if test 1616 -ne `wc -c <'man/nngrab.1'`; then
  86.     echo shar: \"'man/nngrab.1'\" unpacked with wrong size!
  87.   fi
  88.   # end of 'man/nngrab.1'
  89. fi
  90. if test -f 'newsrc.c' -a "${1}" != "-c" ; then 
  91.   echo shar: Will not clobber existing file \"'newsrc.c'\"
  92. else
  93.   echo shar: Extracting \"'newsrc.c'\" \(34671 characters\)
  94.   sed "s/^X//" >'newsrc.c' <<'END_OF_FILE'
  95. X/*
  96. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  97. X *
  98. X *    .newsrc parsing and update.
  99. X */
  100. X
  101. X#include "config.h"
  102. X#include "options.h"
  103. X#include "regexp.h"
  104. X#include "term.h"
  105. X#include "articles.h"
  106. X
  107. X#define TR(arg) printf arg
  108. X
  109. Ximport char *news_lib_directory, *db_directory;
  110. Ximport char *pname;
  111. X
  112. Ximport int  verbose;
  113. Ximport int  silent;
  114. X
  115. Xexport int  keep_rc_backup = 1;
  116. Xexport char *bak_suffix = ".bak";
  117. X
  118. Xexport int  no_update = 0;
  119. Xexport int  use_selections = 1;
  120. Xexport int  quick_unread_count = 1; /* make a quick count of unread art. */
  121. Xexport int  newsrc_update_freq = 1; /* how often to write .newsrc */
  122. Xexport char *newsrc_file = NULL;
  123. X
  124. X#define RCX_NEVER    0 /* ignore missing groups */
  125. X#define RCX_HEAD    1 /* prepend missing groups to .newsrc when read */
  126. X#define    RCX_TAIL    2 /* append missing groups to .newsrc when read */
  127. X#define RCX_TIME    3 /* append NEW groups as they arrive */
  128. X#define RCX_TIME_CONF    4 /* append NEW groups with confirmation */
  129. X#define RCX_RNLAST    5 /* .rnlast compatible functionality */
  130. X
  131. Xexport int  new_group_action = RCX_TIME; /* append new groups to .newsrc */
  132. Xexport int  keep_unsubscribed = 1; /* keep unsubscribed groups in .newsrc */
  133. Xexport int  keep_unsub_long = 0; /* keep unread in unsubscribed groups */
  134. X
  135. Xexport int  tidy_newsrc = 0;       /* remove obsolete groups from .newsrc */
  136. X
  137. Xexport int  auto_junk_seen = 1;    /* junk seen articles ... */
  138. Xexport int  conf_junk_seen = 0; /* ... if confirmed by user ... */
  139. Xexport int  retain_seen_status = 0; /* ... or remember seen articles. */
  140. X
  141. Xexport long unread_articles;    /* estimate of unread articles */
  142. Xexport int  unread_groups;
  143. X
  144. Xexport group_header *rc_sequence = NULL;
  145. X
  146. Xstatic char *sel_path = NULL;
  147. X
  148. X
  149. X/* delimitors on newsrc lines */
  150. X
  151. X#define RC_SUBSCR    ':'    /* subscription to group */
  152. X#define RC_UNSUBSCR    '!'    /* no subscription to group */
  153. X
  154. X#define RC_DELIM    ','    /* separator on rc lines */
  155. X#define RC_RANGE    '-'    /* range */
  156. X
  157. X/* delimitors on select lines */
  158. X
  159. X#define SEL_RANGE    '-'    /* range */
  160. X#define SEL_SELECT    ','    /* following articles are selected */
  161. X#define SEL_LEAVE    '+'    /* following articles are left over */
  162. X#define SEL_SEEN    ';'    /* following articles are seen */
  163. X#define SEL_UNREAD    '~'    /* in digests */
  164. X#define SEL_DIGEST    '('    /* start digest list */
  165. X#define SEL_END_DIGEST    ')'    /* end digest list */
  166. X#define SEL_NEW        '&'    /* new group (group.name&nnn) */
  167. X
  168. X#define END_OF_LIST    10000000L /* Greater than any article number */
  169. X
  170. X/* line buffers */
  171. X
  172. X#define RC_LINE_MAX    8192
  173. X
  174. Xstatic char rcbuf[RC_LINE_MAX];
  175. Xstatic char selbuf[RC_LINE_MAX];
  176. X
  177. Xstatic group_header *rc_seq_tail = NULL;
  178. X
  179. Xstatic int newsrc_update_count = 0, select_update_count = 0;
  180. X
  181. X#define DM_NEWSRC    0
  182. X#define DM_SELECT    1
  183. X#define DM_ORIG_NEWSRC    2
  184. X#define DM_ORIG_SELECT    3
  185. X
  186. Xstatic dump_file(path, mode)
  187. Xchar *path;
  188. Xint mode;
  189. X{
  190. X    FILE *f = NULL;
  191. X    register group_header *gh;
  192. X    char *line;
  193. X
  194. X    Loop_Groups_Newsrc(gh) {
  195. X    switch (mode) {
  196. X     case DM_NEWSRC:
  197. X        if (tidy_newsrc) {
  198. X        if ((gh->master_flag & M_VALID) == 0)
  199. X            continue;
  200. X        if (!keep_unsubscribed && (gh->group_flag & G_UNSUBSCRIBED))
  201. X            continue;
  202. X        }
  203. X        line = gh->newsrc_line;
  204. X        break;
  205. X     case DM_SELECT:
  206. X        if (tidy_newsrc && (gh->master_flag & M_VALID) == 0) continue;
  207. X        if (gh->group_flag & G_UNSUBSCRIBED) continue;
  208. X        line = gh->select_line;
  209. X        break;
  210. X     case DM_ORIG_NEWSRC:
  211. X        line = gh->newsrc_orig;
  212. X        break;
  213. X     case DM_ORIG_SELECT:
  214. X        line = gh->select_orig;
  215. X        break;
  216. X    }
  217. X    if (line == NULL) continue;
  218. X    if (f == NULL)
  219. X        f = open_file(path, OPEN_CREATE|MUST_EXIST);
  220. X    fputs(line, f);
  221. X    }
  222. X    if (f != NULL) fclose(f);
  223. X}
  224. X
  225. X
  226. Xstatic dump_newsrc()
  227. X{
  228. X    char bak[FILENAME];
  229. X    static int first = 1;
  230. X
  231. X    if (no_update) return;
  232. X    if (++newsrc_update_count < newsrc_update_freq) return;
  233. X
  234. X    if (first && keep_rc_backup) {
  235. X    sprintf(bak, "%s%s", newsrc_file, bak_suffix);
  236. X    dump_file(bak, DM_ORIG_NEWSRC);
  237. X    first = 0;
  238. X    }
  239. X
  240. X    dump_file(newsrc_file, DM_NEWSRC);
  241. X
  242. X    newsrc_update_count = 0;
  243. X}
  244. X
  245. Xstatic dump_select()
  246. X{
  247. X    char bak[FILENAME];
  248. X    static int first = 1;
  249. X
  250. X    if (no_update) return;
  251. X    if (++select_update_count < newsrc_update_freq) return;
  252. X
  253. X    if (first && keep_rc_backup) {
  254. X    sprintf(bak, "%s%s", sel_path, bak_suffix);
  255. X    dump_file(bak, DM_ORIG_SELECT);
  256. X    first = 0;
  257. X    }
  258. X
  259. X    dump_file(sel_path, DM_SELECT);
  260. X
  261. X    select_update_count = 0;
  262. X}
  263. X
  264. X#define RN_LAST_GROUP_READ    0
  265. X#define RN_LAST_TIME_RUN    1
  266. X#define RN_LAST_ACTIVE_SIZE    2
  267. X#define RN_LAST_CREATION_TIME    3
  268. X#define RN_LAST_NEW_GROUP    4
  269. X#define RN_ACTIVE_TIMES_OFFSET    5
  270. X
  271. X#define MAX_RNLAST_LINE 6
  272. X
  273. Xstatic char *rnlast_line[MAX_RNLAST_LINE];
  274. Xstatic char *rnlast_path;
  275. X
  276. Xstatic time_t get_last_new()
  277. X{
  278. X    FILE *lf = NULL;
  279. X    char buf[FILENAME];
  280. X    register int i;
  281. X
  282. X    if (new_group_action == RCX_RNLAST) {
  283. X    rnlast_path = home_relative(".rnlast");
  284. X    lf = open_file(rnlast_path, OPEN_READ);
  285. X    if (lf == NULL) goto no_file;
  286. X
  287. X    for (i = 0; i < MAX_RNLAST_LINE; i++) {
  288. X        if (fgets(buf, FILENAME, lf) == NULL) break;
  289. X        rnlast_line[i] = copy_str(buf);
  290. X    }
  291. X    if (i != MAX_RNLAST_LINE) {
  292. X        printf(".rnlast only supported with active.times patches\n");
  293. X        sleep(3);
  294. X        new_group_action = RCX_TIME_CONF;
  295. X        goto no_file;
  296. X    }
  297. X    fclose(lf);
  298. X    return (time_t)atol(rnlast_line[RN_LAST_CREATION_TIME]);
  299. X    }
  300. X
  301. X    lf = open_file(relative(nn_directory, "LAST"), OPEN_READ);
  302. X    if (lf == NULL) goto no_file;
  303. X    if (fgets(buf, FILENAME, lf) == NULL) goto no_file;
  304. X
  305. X    fclose(lf);
  306. X    return (time_t)atol(buf);
  307. X
  308. X no_file:
  309. X    if (lf != NULL) fclose(lf);
  310. X    return (time_t)(-1);
  311. X}
  312. X
  313. Xstatic update_last_new(lastg)
  314. Xgroup_header *lastg;
  315. X{
  316. X    FILE *lf = NULL;
  317. X    register int i;
  318. X    struct stat st;
  319. X
  320. X    if (new_group_action == RCX_RNLAST) {
  321. X    lf = open_file(rnlast_path, OPEN_CREATE|MUST_EXIST);
  322. X    fputs(rnlast_line[RN_LAST_GROUP_READ], lf);    /* as good as any */
  323. X    fprintf(lf, "%ld\n", (long)cur_time()); /* RN_LAST_TIME_RUN */
  324. X    fprintf(lf, "%ld\n", (long)master.last_size); /* RN_LAST_ACTIVE_SIZE */
  325. X
  326. X    fprintf(lf, "%ld\n", (long)lastg->creation_time); /* RN_LAST_CREATION_TIME */
  327. X    fprintf(lf, "%s\n",lastg->group_name); /* RN_LAST_NEW_GROUP */
  328. X
  329. X    if (stat(relative(news_lib_directory, "active.times"), &st) == 0)
  330. X        fprintf(lf, "%ld\n", (long)st.st_size);
  331. X    else /* can't be perfect -- don't update */
  332. X        fputs(rnlast_line[RN_ACTIVE_TIMES_OFFSET], lf);
  333. X    for (i = 0; i < MAX_RNLAST_LINE; i++) freeobj(rnlast_line[i]);
  334. X    freeobj(rnlast_path);
  335. X    } else {
  336. X    lf = open_file(relative(nn_directory, "LAST"), OPEN_CREATE|MUST_EXIST);
  337. X    fprintf(lf, "%ld\n%s\n", (long)lastg->creation_time, lastg->group_name);
  338. X    }
  339. X
  340. X    fclose(lf);
  341. X}
  342. X
  343. Xstatic article_number get_last_article(gh)
  344. Xgroup_header *gh;
  345. X{
  346. X    register char *line;
  347. X
  348. X    if ((line = gh->newsrc_line) == NULL) return -1;
  349. X
  350. X    line += gh->group_name_length+1;
  351. X    while (*line && isspace(*line)) line++;
  352. X    if (*line == NUL) return -1;
  353. X
  354. X    if (line[0] == '1') {
  355. X    if (line[1] == RC_RANGE)
  356. X        return atol(line+2);
  357. X    if (!isdigit(line[1])) return 1;
  358. X    }
  359. X    return 0;
  360. X}
  361. X
  362. X
  363. Xvisit_rc_file()
  364. X{
  365. X    FILE *rc, *sel;
  366. X    register group_header *gh;
  367. X    int subscr;
  368. X    register char *bp;
  369. X    register int c;
  370. X    char bak[FILENAME];
  371. X    time_t last_new_group = 0, rc_age, newsrc_age;
  372. X    group_header *last_new_gh = NULL;
  373. X
  374. X    if (newsrc_file == NULL)
  375. X    newsrc_file = home_relative(".newsrc");
  376. X
  377. X    sel_path = mk_file_name(nn_directory, "select");
  378. X
  379. X    Loop_Groups_Header(gh) {
  380. X    gh->newsrc_line = NULL;
  381. X    gh->newsrc_orig = NULL;
  382. X    gh->select_line = NULL;
  383. X    gh->select_orig = NULL;
  384. X    }
  385. X
  386. X    if (rc_age = file_exist(relative(nn_directory, "rc"), (char *)NULL)) {
  387. X    if (who_am_i != I_AM_NN)
  388. X        user_error("A release 6.3 rc file exists. Run nn to upgrade");
  389. X
  390. X    sprintf(bak, "%s/upgrade_rc", lib_directory);
  391. X
  392. X    if ((newsrc_age = file_exist(newsrc_file, (char *)NULL)) == 0) {
  393. X        display_file("adm.upgrade1", CLEAR_DISPLAY);
  394. X    } else {
  395. X        if (rc_age + 60 > newsrc_age) {
  396. X        /* rc file is newest (or .newsrc does not exist) */
  397. X        display_file("adm.upgrade2", CLEAR_DISPLAY);
  398. X        prompt("Convert rc file to .newsrc now? ");
  399. X        if (yes(1) <= 0) nn_exit(0);
  400. X        } else {
  401. X        /* .newsrc file is newest */
  402. X        display_file("adm.upgrade3", CLEAR_DISPLAY);
  403. X        prompt("Use current .newsrc file? ");
  404. X        if (yes(1) > 0) {
  405. X            strcat(bak, " n");
  406. X        } else {
  407. X            display_file("adm.upgrade4", CLEAR_DISPLAY);
  408. X            prompt("Convert rc file to .newsrc? ");
  409. X            if (yes(1) <= 0) {
  410. X            printf("Then you will have to upgrade manually\n");
  411. X            nn_exit(0);
  412. X            }
  413. X        }
  414. X        }
  415. X    }
  416. X
  417. X    printf("\r\n\n");
  418. X    system(bak);
  419. X    any_key(prompt_line);
  420. X    }
  421. X
  422. X    rc = open_file(newsrc_file, OPEN_READ);
  423. X    if (rc == NULL) goto new_user;
  424. X
  425. X    while (fgets(rcbuf, RC_LINE_MAX, rc) != NULL) {
  426. X    gh = NULL;
  427. X    subscr = 0;
  428. X    for (bp = rcbuf; (c = *bp); bp++) {
  429. X        if (isspace(c)) break;    /* not a valid line */
  430. X
  431. X        if (c == RC_UNSUBSCR || c == RC_SUBSCR) {
  432. X        subscr = (c == RC_SUBSCR);
  433. X        *bp = NUL;
  434. X        gh = lookup(rcbuf);
  435. X        if (gh == NULL) {
  436. X            gh = newobj(group_header, 1);
  437. X            gh->group_name = copy_str(rcbuf); /* invalid group! */
  438. X        }
  439. X        *bp = c;
  440. X        break;
  441. X        }
  442. X    }
  443. X
  444. X    if (gh == NULL) {
  445. X        gh = newobj(group_header, 1);
  446. X        gh->group_flag |= G_FAKED;
  447. X        gh->master_flag |= M_VALID;
  448. X    }
  449. X
  450. X    if (rc_seq_tail == NULL)
  451. X        rc_sequence = rc_seq_tail = gh;
  452. X    else {
  453. X        rc_seq_tail->newsrc_seq = gh;
  454. X        rc_seq_tail = gh;
  455. X    }
  456. X
  457. X    gh->newsrc_orig = gh->newsrc_line = copy_str(rcbuf);
  458. X    if (gh->group_flag & G_FAKED)
  459. X        gh->group_name = gh->newsrc_line;
  460. X    else
  461. X        if (!subscr)
  462. X        gh->group_flag |= G_UNSUBSCRIBED;
  463. X    }
  464. X    fclose(rc);
  465. X
  466. X new_user:
  467. X    rc = NULL;
  468. X    Loop_Groups_Header(gh) {
  469. X    if (gh->master_flag & M_IGNORE_GROUP) continue;
  470. X    if (gh->group_flag & G_UNSUBSCRIBED) continue;
  471. X    if (gh->newsrc_line == NULL) {
  472. X        char buf[FILENAME];
  473. X
  474. X        /* NEW GROUP - ADD TO NEWSRC AS APPROPRIATE */
  475. X
  476. X        if (new_group_action == RCX_NEVER) {
  477. X        gh->group_flag |= G_DONE; /* will not enter sequence */
  478. X        continue;
  479. X        }
  480. X
  481. X        switch (new_group_action) {
  482. X         case RCX_NEVER:
  483. X        /* no not add new groups */
  484. X        gh->group_flag |= G_DONE;
  485. X        continue;
  486. X
  487. X         case RCX_HEAD:
  488. X        /* insert at top */
  489. X        gh->newsrc_seq = rc_sequence;
  490. X        rc_sequence = gh;
  491. X        break;
  492. X
  493. X
  494. X         case RCX_TIME:
  495. X         case RCX_TIME_CONF:
  496. X         case RCX_RNLAST:
  497. X        if (last_new_group == 0)
  498. X            last_new_group = get_last_new();
  499. X
  500. X        if (gh->creation_time <= last_new_group) {
  501. X            /* old groups not in .newsrc are unsubscribed */
  502. X            gh->group_flag |= G_UNSUBSCRIBED;
  503. X            continue;
  504. X        }
  505. X
  506. X        if (last_new_gh == NULL || last_new_gh->creation_time <= gh->creation_time)
  507. X            last_new_gh = gh;
  508. X
  509. X        if (new_group_action != RCX_TIME) {
  510. X            printf("\nNew group: %s -- append to .newsrc? (y)");
  511. X            if (yes(0) <= 0) continue;
  512. X        }
  513. X        sprintf(buf, "%s:\n", gh->group_name);
  514. X        /* to avoid fooling the LAST mechanism, we must fake */
  515. X        /* that the group was also in the original .newsrc */
  516. X
  517. X        gh->newsrc_orig = gh->newsrc_line = copy_str(buf);
  518. X        newsrc_update_count++;
  519. X
  520. X        /* fall thru */
  521. X
  522. X         case RCX_TAIL:
  523. X        /* insert at bottom */
  524. X        if (rc_seq_tail == NULL)
  525. X            rc_sequence = rc_seq_tail = gh;
  526. X        else {
  527. X            rc_seq_tail->newsrc_seq = gh;
  528. X            rc_seq_tail = gh;
  529. X        }
  530. X        break;
  531. X        }
  532. X
  533. X        gh->last_article = -1;
  534. X    } else
  535. X        gh->last_article = get_last_article(gh);
  536. X
  537. X    if (gh->last_article < 0) {
  538. X        gh->group_flag |= G_NEW;
  539. X        gh->last_article = gh->first_db_article - 1;
  540. X    } else
  541. X        if (gh->first_db_article > gh->last_article)
  542. X        gh->last_article = gh->first_db_article - 1;
  543. X
  544. X    if (gh->last_article < 0) gh->last_article = 0;
  545. X    gh->first_article = gh->last_article;
  546. X    }
  547. X
  548. X    if (rc_seq_tail)
  549. X    rc_seq_tail->newsrc_seq = NULL;
  550. X
  551. X    if (last_new_gh != NULL)
  552. X    update_last_new(last_new_gh);
  553. X
  554. X    if (!use_selections) return;
  555. X
  556. X    sel = open_file(sel_path, OPEN_READ);
  557. X    if (sel == NULL) return;
  558. X
  559. X    while (fgets(selbuf, RC_LINE_MAX, sel) != NULL) {
  560. X    gh = NULL;
  561. X    for (bp = selbuf; (c = *bp); bp++)
  562. X        if (c == SP || c == SEL_NEW) break;
  563. X
  564. X    if (c == NUL) continue;
  565. X    *bp = NUL;
  566. X    gh = lookup(selbuf);
  567. X    if (gh == NULL) continue;
  568. X    *bp = c;
  569. X    if (c == SEL_NEW) gh->group_flag |= G_NEW;
  570. X    gh->select_orig = gh->select_line = copy_str(selbuf);
  571. X    }
  572. X    fclose(sel);
  573. X}
  574. X
  575. X/*
  576. X * prepare to use newsrc & select information for a specific group
  577. X */
  578. X
  579. Xstatic char *rc_p;        /* pointer into newsrc_line */
  580. Xstatic article_number rc_min;    /* current newsrc range min */
  581. Xstatic article_number rc_max;    /* current newsrc range max */
  582. Xstatic char rc_delim;        /* delimiter character */
  583. X
  584. Xstatic char *sel_p;        /* pointer into select_line */
  585. Xstatic char *sel_initp;        /* rc_p after initialization */
  586. Xstatic article_number sel_min;    /* current select range min */
  587. Xstatic article_number sel_max;    /* current select range max */
  588. Xstatic article_number sel_digest; /* current digest */
  589. Xstatic attr_type sel_type;    /* current select range type */
  590. Xstatic char sel_delim;        /* delimiter character */
  591. X
  592. X
  593. Xuse_newsrc(gh, use_orig)
  594. Xregister group_header *gh;
  595. Xint use_orig;
  596. X{
  597. X/*    TR( ("===%s===", gh->group_name) );*/
  598. X
  599. X    if (use_orig) {
  600. X    rc_p = gh->newsrc_orig;
  601. X    sel_p = gh->select_orig;
  602. X    } else {
  603. X    rc_p = gh->newsrc_line;
  604. X    sel_p = gh->select_line;
  605. X    }
  606. X
  607. X    if (rc_p == NULL) {
  608. X    rc_min = rc_max = END_OF_LIST;
  609. X    } else {
  610. X    rc_min = rc_max = -1;
  611. X    rc_delim = SP;
  612. X    rc_p += gh->group_name_length + 1;
  613. X    }
  614. X
  615. X    sel_digest = 0;
  616. X    if (sel_p == NULL) {
  617. X    sel_min = sel_max = END_OF_LIST;
  618. X    } else {
  619. X    sel_p += gh->group_name_length + 1;
  620. X    sel_min = sel_max = -1;
  621. X    sel_delim = SP;
  622. X    }
  623. X}
  624. X/*
  625. X#define TRC(wh)  TR( ("r%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, rc_min, rc_max, rc_delim) )
  626. X#define TSEL(wh) TR( ("s%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, sel_min, sel_max, sel_delim) )
  627. X*/
  628. X#define TRC(wh)
  629. X#define TSEL(wh)
  630. X
  631. Xattr_type test_article(ah)
  632. Xregister article_header *ah;
  633. X{
  634. X    register char *p;
  635. X    register int c;
  636. X    register int32 n = ah->a_number, x;
  637. X
  638. X    while (n > rc_max) {
  639. X    /* get next interval from newsrc line */
  640. X    rc_min = -1;
  641. X    x = 0;
  642. X    p = rc_p;
  643. X    TRC(1);
  644. X
  645. X    if (*p == RC_DELIM) p++;
  646. X    if (*p == NUL || *p == NL)
  647. X        rc_min = rc_max = END_OF_LIST;
  648. X    else {
  649. X        for ( ; (c = *p) && c != RC_DELIM && c != NL; p++) {
  650. X        if (c == RC_RANGE) {
  651. X            if (rc_min < 0)
  652. X            rc_min = x;
  653. X            else
  654. X            msg("syntax error in rc file");
  655. X            x = 0;
  656. X            continue;
  657. X        }
  658. X
  659. X        if (isascii(*p) && isdigit(*p))
  660. X            x = x*10 + c - '0';
  661. X        }
  662. X        rc_max = x;
  663. X        if (rc_min < 0) rc_min = x;
  664. X        rc_p = p;
  665. X    }
  666. X    }
  667. X    TRC(2);
  668. X
  669. X    if (n >= rc_min && n <= rc_max) return A_READ;
  670. X
  671. X    p = sel_p;
  672. X    if (sel_digest != 0) {
  673. X    if (n == sel_digest && (ah->flag & A_DIGEST)) {
  674. X        if (*sel_p == SEL_END_DIGEST) return A_READ;
  675. X        n = ah->fpos;
  676. X    } else {
  677. X        if (n < sel_digest) return 0;
  678. X        while (*p && *p++ != SEL_END_DIGEST);
  679. X        sel_digest = 0;
  680. X        sel_min = sel_max = -1;
  681. X    }
  682. X    }
  683. X
  684. X    while (n > sel_max) {
  685. X    sel_min = -1;
  686. X    sel_type = A_SELECT;
  687. X    x = 0;
  688. X    TSEL(3);
  689. X
  690. X    for (;;) {
  691. X        switch (*p) {
  692. X         case SEL_SELECT:
  693. X        sel_type = A_SELECT;
  694. X        p++;
  695. X        continue;
  696. X         case SEL_LEAVE:
  697. X        sel_type = A_LEAVE;
  698. X        p++;
  699. X        continue;
  700. X         case SEL_SEEN:
  701. X        sel_type = A_SEEN;
  702. X        p++;
  703. X        continue;
  704. X         case SEL_UNREAD:
  705. X        sel_type = 0;
  706. X        p++;
  707. X        continue;
  708. X         case SEL_DIGEST:
  709. X        while (*p && *p++ != SEL_END_DIGEST);
  710. X        continue;
  711. X         case SEL_END_DIGEST:
  712. X        if (sel_digest) {
  713. X            if (sel_digest == ah->a_number) {
  714. X            sel_p = p;
  715. X            return A_READ;
  716. X            }
  717. X            sel_digest = 0;
  718. X        }
  719. X        p++;
  720. X        sel_type = A_SELECT;
  721. X        continue;
  722. X         default:
  723. X        break;
  724. X        }
  725. X        break;
  726. X    }
  727. X
  728. X    if (*p == NUL || *p == NL) {
  729. X        sel_min = sel_max = END_OF_LIST;
  730. X        break;
  731. X    }
  732. X
  733. X    for ( ; c = *p ; p++ ) {
  734. X        switch (c) {
  735. X         case '0':
  736. X         case '1':
  737. X         case '2':
  738. X         case '3':
  739. X         case '4':
  740. X         case '5':
  741. X         case '6':
  742. X         case '7':
  743. X         case '8':
  744. X         case '9':
  745. X        x = x*10 + c - '0';
  746. X        continue;
  747. X
  748. X         case SEL_SELECT:
  749. X         case SEL_LEAVE:
  750. X         case SEL_SEEN:
  751. X         case SEL_UNREAD:
  752. X        break;
  753. X
  754. X         case SEL_RANGE:
  755. X        if (sel_min < 0)
  756. X            sel_min = x;
  757. X        else
  758. X            msg("syntax error in sel file");
  759. X        x = 0;
  760. X        continue;
  761. X
  762. X         case SEL_DIGEST:
  763. X        n = ah->a_number;
  764. X        if (n > x) {
  765. X            while (*p && (*p++ != SEL_END_DIGEST));
  766. X            x = -1;
  767. X            break;
  768. X        }
  769. X        p++;
  770. X        sel_digest = x;
  771. X        if (n < sel_digest) {
  772. X            sel_p = p;
  773. X            return 0;
  774. X        }
  775. X        n = ah->fpos;
  776. X        x = -1;
  777. X        break;
  778. X
  779. X         case NL:
  780. X        if (sel_digest == 0) break;
  781. X        /* fall thru */
  782. X         case SEL_END_DIGEST:
  783. X        if (sel_digest == ah->a_number) {
  784. X            sel_p = p;
  785. X            return (ah->fpos == x) ? sel_type : A_READ;
  786. X        }
  787. X        sel_digest = 0;
  788. X        x = -1;
  789. X        break;
  790. X        }
  791. X        break;
  792. X    }
  793. X    sel_max = x;
  794. X    if (sel_min < 0) sel_min = x;
  795. X    sel_p = p;
  796. X    }
  797. X
  798. X    if (n >= sel_min && n <= sel_max) return sel_type;
  799. X
  800. X    if (sel_digest) return A_READ; /* only read articles are not listed */
  801. X
  802. X    return 0;    /* unread, unseen, unselected */
  803. X}
  804. X
  805. X/*
  806. X * We only mark the articles that should remain unread
  807. X */
  808. X
  809. X/*VARARGS*/
  810. Xstatic append(va_alist)
  811. Xva_dcl
  812. X{
  813. X    int x;
  814. X    register char *p;
  815. X    char **pp, *fmt;
  816. X    use_vararg;
  817. X
  818. X    start_vararg;
  819. X    x = va_arg1(int);
  820. X    pp = x ? &sel_p : &rc_p;
  821. X    p = *pp;
  822. X    if (p > (x ? &selbuf[RC_LINE_MAX - 16] : &rcbuf[RC_LINE_MAX - 16])) {
  823. X    msg("%s line too long", x ? "select" : ".newsrc");
  824. X    end_vararg;
  825. X    return;
  826. X    }
  827. X    fmt = va_arg2(char *);
  828. X    vsprintf(p, fmt, va_args3toN);
  829. X    end_vararg;
  830. X
  831. X    while (*p) p++;
  832. X    *p = NL;
  833. X    p[1] = NUL;
  834. X    *pp = p;
  835. X}
  836. X
  837. Xstatic append_range(pp, delim, rmin, rmax)
  838. Xint pp;
  839. Xchar delim;
  840. Xarticle_number rmin, rmax;
  841. X{
  842. X    if (rmin == rmax)
  843. X    append(pp, "%c%ld", delim, (long)rmin);
  844. X    else
  845. X    append(pp, "%c%ld%c%ld", delim, (long)rmin, RC_RANGE, (long)rmax);
  846. X}
  847. X
  848. Xstatic int32 mark_counter;
  849. X
  850. Xstatic begin_rc_update(gh)
  851. Xregister group_header *gh;
  852. X{
  853. X    add_unread(gh, -1);
  854. X    mark_counter = 0;
  855. X
  856. X    rc_p = rcbuf;
  857. X    rc_min = 1;
  858. X    append(0, "%s%c", gh->group_name,
  859. X       gh->group_flag & G_UNSUBSCRIBED ? RC_UNSUBSCR : RC_SUBSCR);
  860. X    rc_delim = SP;
  861. X    sel_p = selbuf;
  862. X    sel_min = 0;
  863. X    sel_max = 0;
  864. X    sel_digest = 0;
  865. X    sel_delim = SP;
  866. X    append(1, "%s%c", gh->group_name,
  867. X       (gh->group_flag & G_NEW) ? SEL_NEW : SP);
  868. X    /* sel_initp == sep_p => empty list */
  869. X    sel_initp = (gh->group_flag & G_NEW) ? NULL : sel_p;
  870. X}
  871. X
  872. Xstatic end_rc_update(gh)
  873. Xregister group_header *gh;
  874. X{
  875. X    if (rc_min <= gh->last_db_article)
  876. X    append_range(0, rc_delim, rc_min, gh->last_db_article);
  877. X
  878. X    if (gh->newsrc_line != NULL && strcmp(rcbuf, gh->newsrc_line)) {
  879. X    if (gh->newsrc_orig != gh->newsrc_line)
  880. X        freeobj(gh->newsrc_line);
  881. X    gh->newsrc_line = NULL;
  882. X    }
  883. X
  884. X    if (gh->newsrc_line == NULL) {
  885. X    gh->newsrc_line = copy_str(rcbuf);
  886. X    dump_newsrc();
  887. X    }
  888. X
  889. X    if (sel_digest)
  890. X    append(1, "%c", SEL_END_DIGEST);
  891. X    else
  892. X    if (sel_min)
  893. X        append_range(1, sel_delim, sel_min, sel_max);
  894. X
  895. X    if (gh->select_line) {
  896. X    if (strcmp(selbuf, gh->select_line) == 0) goto out;
  897. X    } else
  898. X        if (sel_p == sel_initp) goto out;
  899. X
  900. X    if (gh->select_line && gh->select_orig != gh->select_line)
  901. X    freeobj(gh->select_line);
  902. X
  903. X    gh->select_line = (sel_p == sel_initp) ? NULL : copy_str(selbuf);
  904. X    dump_select();
  905. X
  906. X out:
  907. X    if ((gh->last_article = get_last_article(gh)) < 0)
  908. X    gh->last_article = 0;
  909. X
  910. X    gh->group_flag |= G_READ;    /* should not call update_group again */
  911. X    if (mark_counter > 0) {
  912. X    gh->unread_count = mark_counter;
  913. X    add_unread(gh, 0);
  914. X    }
  915. X}
  916. X
  917. Xstatic mark_article(ah, how)
  918. Xregister article_header *ah;
  919. Xattr_type how;
  920. X{
  921. X    register article_number anum;
  922. X    char delim;
  923. X
  924. X    switch (how) {
  925. X     case A_SELECT:
  926. X    delim = SEL_SELECT;
  927. X    break;
  928. X     case A_SEEN:
  929. X    delim = SEL_SEEN;
  930. X    break;
  931. X     case A_LEAVE:
  932. X     case A_LEAVE_NEXT:
  933. X    delim = SEL_LEAVE;
  934. X    break;
  935. X     case 0:
  936. X    delim = SEL_UNREAD;
  937. X    break;
  938. X    }
  939. X
  940. X    mark_counter++;
  941. X    anum = ah->a_number;
  942. X
  943. X    if (rc_min < anum) {
  944. X    append_range(0, rc_delim, rc_min, anum - 1);
  945. X    rc_delim = RC_DELIM;
  946. X
  947. X    if ((ah->flag & A_DIGEST) == 0
  948. X        && sel_min && delim == sel_delim && sel_max == (rc_min - 1))
  949. X        sel_max = anum - 1;    /* expand select range over read articles */
  950. X    }
  951. X    rc_min = anum + 1;
  952. X
  953. X    if (ah->flag & A_DIGEST) {
  954. X    if (sel_digest != anum) {
  955. X        if (sel_digest) {
  956. X        append(1, "%c", SEL_END_DIGEST);
  957. X        } else
  958. X        if (sel_min) {
  959. X            append_range(1, sel_delim, sel_min, sel_max);
  960. X            sel_min = 0;
  961. X        }
  962. X        append(1, "%c%ld%c", SEL_SELECT, (long)anum, SEL_DIGEST);
  963. X        sel_digest = anum;
  964. X    }
  965. X
  966. X    append(1, "%c%ld", delim, (long)ah->fpos);
  967. X    return;
  968. X    }
  969. X
  970. X    if (sel_digest) {
  971. X    append(1, "%c", SEL_END_DIGEST);
  972. X    sel_digest = 0;
  973. X    }
  974. X
  975. X    if (sel_min) {
  976. X    if (delim != sel_delim || delim == SEL_UNREAD) {
  977. X        append_range(1, sel_delim, sel_min, sel_max);
  978. X        sel_delim = delim;
  979. X        if (delim == SEL_UNREAD)
  980. X        sel_min = 0;
  981. X        else
  982. X        sel_min = anum;
  983. X    } else
  984. X        sel_max = anum;
  985. X    } else
  986. X    if (delim != SEL_UNREAD) {
  987. X        sel_min = sel_max = anum;
  988. X        sel_delim = delim;
  989. X    }
  990. X}
  991. X
  992. Xflush_newsrc()
  993. X{
  994. X    newsrc_update_freq = 0;
  995. X    if (select_update_count) dump_select();
  996. X    if (newsrc_update_count) dump_newsrc();
  997. X}
  998. X
  999. Xrestore_bak()
  1000. X{
  1001. X    if (no_update)
  1002. X    return 1;
  1003. X
  1004. X    prompt("Are you sure? ");
  1005. X    if (!yes(1)) return 0;
  1006. X
  1007. X    dump_file(newsrc_file, DM_ORIG_NEWSRC);
  1008. X
  1009. X    prompt("Restore selections? ");
  1010. X    if (yes(1)) dump_file(sel_path, DM_ORIG_SELECT);
  1011. X
  1012. X    no_update = 1;    /* so current group is not updated */
  1013. X    return 1;
  1014. X}
  1015. X
  1016. X/*
  1017. X *    Update .newsrc for one group.
  1018. X *    sort_articles(0) MUST HAVE BEEN CALLED BEFORE USE.
  1019. X */
  1020. X
  1021. Xupdate_rc(gh)
  1022. Xregister group_header *gh;
  1023. X{
  1024. X    register article_header *ah, **ahp;
  1025. X    register article_number art;
  1026. X    register int junk_seen = 0;
  1027. X
  1028. X    if (gh->group_flag & (G_FOLDER | G_FAKED)) return;
  1029. X
  1030. X    begin_rc_update(gh);
  1031. X
  1032. X    for (ahp = articles, art = 0; art < n_articles; ahp++, art++) {
  1033. X    ah = *ahp;
  1034. X    if (ah->a_group != NULL && ah->a_group != gh) continue;
  1035. X
  1036. X    switch (ah->attr) {
  1037. X     case A_READ:
  1038. X     case A_KILL:
  1039. X        continue;
  1040. X
  1041. X     case A_LEAVE:
  1042. X     case A_LEAVE_NEXT:
  1043. X     case A_SELECT:
  1044. X        mark_article(ah, ah->attr);
  1045. X        continue;
  1046. X
  1047. X     case A_SEEN:
  1048. X        if (junk_seen == 0) {
  1049. X        junk_seen = -1;
  1050. X        if (auto_junk_seen) {
  1051. X            if (conf_junk_seen) {
  1052. X            prompt("\1Junk seen articles\1 ");
  1053. X            if (yes(0) > 0) junk_seen = 1;
  1054. X            } else
  1055. X            junk_seen = 1;
  1056. X        }
  1057. X        }
  1058. X        if (junk_seen > 0) continue;
  1059. X        mark_article(ah, (attr_type)(retain_seen_status ? A_SEEN : 0));
  1060. X        continue;
  1061. X
  1062. X     case A_AUTO_SELECT:
  1063. X     default:
  1064. X        mark_article(ah, (attr_type)0);
  1065. X        continue;
  1066. X    }
  1067. X    }
  1068. X
  1069. X    end_rc_update(gh);
  1070. X}
  1071. X
  1072. Xupdate_rc_all(gh, unsub)
  1073. Xregister group_header *gh;
  1074. Xint unsub;
  1075. X{
  1076. X    if (unsub) {
  1077. X    gh->group_flag &= ~G_NEW;
  1078. X    gh->group_flag |= G_UNSUBSCRIBED;
  1079. X
  1080. X    if (!keep_unsubscribed) {
  1081. X        add_unread(gh, -1);
  1082. X        if (gh->newsrc_line != NULL && gh->newsrc_orig != gh->newsrc_line)
  1083. X        freeobj(gh->newsrc_line);
  1084. X        gh->newsrc_line = NULL;
  1085. X        return;
  1086. X    }
  1087. X    
  1088. X    if (keep_unsub_long) {
  1089. X        update_rc(gh);
  1090. X        return;
  1091. X    }
  1092. X    }
  1093. X
  1094. X    begin_rc_update(gh);
  1095. X    end_rc_update(gh);
  1096. X}
  1097. X
  1098. Xadd_to_newsrc(gh)
  1099. Xgroup_header *gh;
  1100. X{
  1101. X    gh->group_flag &= ~G_UNSUBSCRIBED;
  1102. X
  1103. X    if (gh->newsrc_seq != NULL || gh == rc_seq_tail) {
  1104. X    update_rc(gh);
  1105. X    return;
  1106. X    }
  1107. X    
  1108. X    rc_seq_tail->newsrc_seq = gh;
  1109. X    rc_seq_tail = gh;
  1110. X    if (gh->last_db_article > 0)
  1111. X    sprintf(rcbuf, "%s: %s%ld\n", gh->group_name,
  1112. X        gh->last_db_article > 1 ? "1-" : "",
  1113. X        (long)gh->last_db_article);
  1114. X    else
  1115. X    sprintf(rcbuf, "%s:\n", gh->group_name);
  1116. X    gh->newsrc_line = copy_str(rcbuf);
  1117. X    dump_newsrc();
  1118. X}
  1119. X
  1120. Xint32 restore_rc(gh, last)
  1121. Xregister group_header *gh;
  1122. Xarticle_number last;
  1123. X{
  1124. X    register article_number *numtab, n;
  1125. X    register attr_type *attrtab, attr;
  1126. X    register int32 at, atmax;
  1127. X    article_header ahdr;
  1128. X    int32 count;
  1129. X
  1130. X    if (last > gh->last_db_article) return 0;
  1131. X
  1132. X    if (gh->unread_count <= 0) {
  1133. X    /* no unread articles to account for -- quick update */
  1134. X    n = gh->last_db_article;    /* fake for end_rc_update */
  1135. X    gh->last_db_article = last;
  1136. X    begin_rc_update(gh);
  1137. X    end_rc_update(gh);
  1138. X    gh->last_db_article = n;
  1139. X    add_unread(gh, 1); /* not done by end_rc_update bec. mark_counter==0 */
  1140. X    return gh->unread_count;
  1141. X    }
  1142. X
  1143. X    /* there are unread articles in the group */
  1144. X    /* we must truncate rc&select lines to retain older unread articles */
  1145. X
  1146. X    atmax = at = 0;
  1147. X    numtab = NULL;
  1148. X    attrtab = NULL;
  1149. X    
  1150. X    use_newsrc(gh, 0);
  1151. X    ahdr.flag = 0;
  1152. X    count = gh->unread_count;
  1153. X
  1154. X    for (n = gh->last_article + 1; n <= last; n++) {
  1155. X    if (rc_min == END_OF_LIST) {
  1156. X        /* current & rest is unread */
  1157. X        last = n - 1;
  1158. X        break;
  1159. X    }
  1160. X    ahdr.a_number = n;
  1161. X    if ((attr = test_article(&ahdr)) == A_READ) continue;
  1162. X    if (at >= atmax) {
  1163. X        atmax += 100;
  1164. X        numtab = resizeobj(numtab, article_number, atmax);
  1165. X        attrtab = resizeobj(attrtab, attr_type, atmax);
  1166. X    }
  1167. X    numtab[at] = n;
  1168. X    attrtab[at] = attr;
  1169. X    at++;
  1170. X    }
  1171. X
  1172. X    begin_rc_update(gh);
  1173. X    while (--at >= 0) {
  1174. X    ahdr.a_number = *numtab++;
  1175. X    mark_article(&ahdr, *attrtab++);
  1176. X    }
  1177. X    for (n = last+1; n <= gh->last_db_article; n++) {
  1178. X    ahdr.a_number = n;
  1179. X    mark_article(&ahdr, (attr_type)0);
  1180. X    }
  1181. X    end_rc_update(gh);
  1182. X    return gh->unread_count - count;
  1183. X}
  1184. X
  1185. Xrestore_unread(gh)
  1186. Xregister group_header *gh;
  1187. X{
  1188. X    if (gh->select_line != gh->select_orig) {
  1189. X    if (gh->select_line != NULL) freeobj(gh->select_line);
  1190. X    gh->select_line = gh->select_orig;
  1191. X    dump_select();
  1192. X    }
  1193. X
  1194. X    if (gh->newsrc_orig == gh->newsrc_line) return 0;
  1195. X
  1196. X    add_unread(gh, -1);
  1197. X    if (gh->newsrc_line != NULL) freeobj(gh->newsrc_line);
  1198. X    gh->newsrc_line = gh->newsrc_orig;
  1199. X    gh->last_article = gh->first_article;
  1200. X    dump_newsrc();
  1201. X
  1202. X    add_unread(gh, 1);
  1203. X
  1204. X    return 1;
  1205. X}
  1206. X
  1207. X
  1208. Xcount_unread_articles()
  1209. X{
  1210. X    register group_header *gh;
  1211. X    long n;
  1212. X
  1213. X    unread_articles = 0;
  1214. X    unread_groups = 0;
  1215. X
  1216. X    Loop_Groups_Sequence(gh) {
  1217. X    gh->unread_count = 0;
  1218. X
  1219. X    if (gh->master_flag & M_NO_DIRECTORY) continue;
  1220. X
  1221. X    if (gh->last_db_article > gh->last_article) {
  1222. X        n = unread_articles;
  1223. X        add_unread(gh, 1);
  1224. X    }
  1225. X    
  1226. X    if ((gh->group_flag & G_COUNTED) == 0) continue;
  1227. X    if (verbose)
  1228. X        printf("%6d %s\n", unread_articles - n, gh->group_name);
  1229. X    }
  1230. X}
  1231. X
  1232. X
  1233. Xprt_unread(format)
  1234. Xregister char *format;
  1235. X{
  1236. X    if (format == NULL) {
  1237. X    printf("No News (is good news)\n");
  1238. X    return;
  1239. X    }
  1240. X
  1241. X    while (*format) {
  1242. X    if (*format != '%') {
  1243. X        putchar(*format++);
  1244. X        continue;
  1245. X    }
  1246. X    format++;
  1247. X    switch (*format++) {
  1248. X     case 'u':
  1249. X        printf("%ld unread article%s", unread_articles, plural((long)unread_articles));
  1250. X        continue;
  1251. X     case 'g':
  1252. X        printf("%d group%s", unread_groups, plural((long)unread_groups));
  1253. X        continue;
  1254. X     case 'i':
  1255. X        printf(unread_articles == 1 ? "is" : "are");
  1256. X        continue;
  1257. X     case 'U':
  1258. X        printf("%ld", unread_articles);
  1259. X        continue;
  1260. X     case 'G':
  1261. X        printf("%d", unread_groups);
  1262. X        continue;
  1263. X    }
  1264. X    }
  1265. X}
  1266. X
  1267. X
  1268. Xadd_unread(gh, mode)
  1269. Xgroup_header *gh;
  1270. Xint mode;    /* +1 => count + add, 0 => gh->unread_count, -1 => subtract */
  1271. X{
  1272. X    int32 old_count;
  1273. X    article_header ahdr;
  1274. X
  1275. X    old_count = gh->unread_count;
  1276. X
  1277. X    if (mode == 0) goto add_directly;
  1278. X
  1279. X    if (gh->group_flag & G_COUNTED) {
  1280. X    unread_articles -= gh->unread_count;
  1281. X    unread_groups --;
  1282. X    gh->unread_count = 0;
  1283. X    gh->group_flag &= ~G_COUNTED;
  1284. X    }
  1285. X
  1286. X    if (mode < 0) goto out;
  1287. X
  1288. X    if (quick_unread_count)
  1289. X    gh->unread_count = gh->last_db_article - gh->last_article;
  1290. X    else {
  1291. X    use_newsrc(gh, 0);
  1292. X    ahdr.flag = 0;
  1293. X    for (ahdr.a_number = gh->last_article + 1;
  1294. X         ahdr.a_number <= gh->last_db_article;
  1295. X         ahdr.a_number++) {
  1296. X        if (rc_min == END_OF_LIST) {
  1297. X        gh->unread_count += gh->last_db_article - ahdr.a_number + 1;
  1298. X        break;
  1299. X        }
  1300. X        if (test_article(&ahdr) != A_READ)
  1301. X        gh->unread_count++;
  1302. X    }
  1303. X    }
  1304. X
  1305. X add_directly:
  1306. X    if (gh->unread_count <= 0) {
  1307. X    gh->unread_count = 0;
  1308. X    goto out;
  1309. X    }
  1310. X
  1311. X    if (gh->group_flag & G_UNSUBSCRIBED) goto out;
  1312. X
  1313. X    unread_articles += gh->unread_count;
  1314. X    unread_groups++;
  1315. X    gh->group_flag |= G_COUNTED;
  1316. X    
  1317. X out:
  1318. X    return old_count != gh->unread_count;
  1319. X}
  1320. X
  1321. X/*
  1322. X *    nngrep
  1323. X */
  1324. X
  1325. Xstatic int
  1326. X    grep_all = 0,
  1327. X    grep_new = 0,
  1328. X    grep_not_sequence = 0,
  1329. X    grep_pending = 0,
  1330. X    grep_read = 0,
  1331. X    grep_sequence = 0,
  1332. X    grep_unsub = 0,
  1333. X    grep_long = 0,
  1334. X    grep_patterns;
  1335. X
  1336. XOption_Description(grep_options) {
  1337. X    'a', Bool_Option(grep_all),
  1338. X    'i', Bool_Option(grep_not_sequence),
  1339. X    'n', Bool_Option(grep_new),
  1340. X    'p', Bool_Option(grep_pending),
  1341. X    'r', Bool_Option(grep_read),
  1342. X    's', Bool_Option(grep_sequence),
  1343. X    'u', Bool_Option(grep_unsub),
  1344. X    'l', Bool_Option(grep_long),
  1345. X    '\0',
  1346. X};
  1347. X
  1348. Xopt_nngrep(argc, argv)
  1349. Xint argc;
  1350. Xchar *argv[];
  1351. X{
  1352. X    grep_patterns =
  1353. X    parse_options(argc, argv, (char *)NULL, grep_options, " pattern...");
  1354. X}
  1355. X
  1356. Xdo_grep(pat)
  1357. Xchar **pat;
  1358. X{
  1359. X    register group_header *gh;
  1360. X    register regexp **re;
  1361. X    register int i;
  1362. X    int header = 1;
  1363. X
  1364. X    re = newobj(regexp *, grep_patterns);
  1365. X    for (i = 0; i < grep_patterns; i++)
  1366. X    re[i] = regcomp(pat[i]);
  1367. X
  1368. X    Loop_Groups_Sorted(gh) {
  1369. X    if (gh->master_flag & M_IGNORE_GROUP) continue;
  1370. X
  1371. X    if (grep_pending && gh->unread_count <= 0) continue;
  1372. X    if (grep_read && gh->unread_count > 0) continue;
  1373. X    if (grep_sequence && (gh->group_flag & G_SEQUENCE) == 0) continue;
  1374. X    if (grep_not_sequence && (gh->group_flag & G_SEQUENCE)) continue;
  1375. X    if (grep_new && (gh->group_flag & G_NEW) == 0) continue;
  1376. X    if (!grep_all) {
  1377. X        if (grep_unsub && (gh->group_flag & G_UNSUBSCRIBED) == 0) continue;
  1378. X        if (!grep_unsub && (gh->group_flag & G_UNSUBSCRIBED)) continue;
  1379. X    }
  1380. X
  1381. X    if (grep_patterns > 0) {
  1382. X        for (i = 0; i < grep_patterns; i++)
  1383. X        if (regexec(re[i], gh->group_name)) break;
  1384. X        if (i == grep_patterns) continue;
  1385. X    }
  1386. X
  1387. X    if (grep_long) {
  1388. X        if (header)
  1389. X        printf("SUBSCR NEW UNREAD SEQUENCE GROUP\n");
  1390. X        header = 0;
  1391. X
  1392. X        printf(" %s   %s ",
  1393. X           (gh->group_flag & G_UNSUBSCRIBED) ? "no " : "yes",
  1394. X           (gh->group_flag & G_NEW) ? "yes" : "no ");
  1395. X
  1396. X        if (gh->unread_count > 0)
  1397. X        printf("%6d ", gh->unread_count);
  1398. X        else
  1399. X        printf("       ");
  1400. X        if (gh->group_flag & G_SEQUENCE)
  1401. X        printf("  %4d   ", gh->preseq_index);
  1402. X        else
  1403. X        printf("         ");
  1404. X    }
  1405. X
  1406. X    printf("%s\n", gh->group_name);
  1407. X    }
  1408. X}
  1409. X
  1410. X
  1411. X/*
  1412. X *    nntidy
  1413. X */
  1414. X
  1415. Xstatic int
  1416. X    tidy_unsubscribed = 0,    /* truncate lines for unsub groups*/
  1417. X    tidy_remove_unsub = 0,    /* remove lines for unsub groups*/
  1418. X    tidy_sequence = 0,        /* remove groups not in sequence */
  1419. X    tidy_ignored = 0,        /* remove G_IGN groups */
  1420. X    tidy_crap = 0,        /* remove unrecognized lines */
  1421. X    tidy_all = 0;        /* all of the above */
  1422. X
  1423. XOption_Description(tidy_options) {
  1424. X    'N', Bool_Option(no_update),
  1425. X    'Q', Bool_Option(silent),
  1426. X    'v', Bool_Option(verbose),
  1427. X    'a', Bool_Option(tidy_all),
  1428. X    'c', Bool_Option(tidy_crap),
  1429. X    'i', Bool_Option(tidy_ignored),
  1430. X    'r', Bool_Option(tidy_remove_unsub),
  1431. X    's', Bool_Option(tidy_sequence),
  1432. X    'u', Bool_Option(tidy_unsubscribed),
  1433. X    '\0',
  1434. X};
  1435. X
  1436. Xopt_nntidy(argc, argv)
  1437. Xint argc;
  1438. Xchar *argv[];
  1439. X{
  1440. X    return parse_options(argc, argv, (char *)NULL, 
  1441. X             tidy_options, " [group]...");
  1442. X}
  1443. X
  1444. Xdo_tidy_newsrc()
  1445. X{
  1446. X    register group_header *gh;
  1447. X    int changed;
  1448. X    char *why;
  1449. X
  1450. X    /* visit_rc_file has been called. */
  1451. X
  1452. X    keep_rc_backup = 1;
  1453. X    bak_suffix = ".tidy";
  1454. X
  1455. X    tidy_newsrc = 0;
  1456. X    changed = 0;
  1457. X
  1458. X    if (tidy_all)
  1459. X    tidy_sequence = tidy_ignored = tidy_crap = tidy_unsubscribed = 1;
  1460. X
  1461. X    newsrc_update_freq = 9999;
  1462. X
  1463. X    Loop_Groups_Newsrc(gh) {
  1464. X    if ((gh->master_flag & M_VALID) == 0) {
  1465. X        why = "Unknown group:   ";
  1466. X        goto delete;
  1467. X    }
  1468. X    if (tidy_sequence && (gh->group_flag & G_SEQUENCE) == 0) {
  1469. X        why = "Not in sequence: ";
  1470. X        goto delete;
  1471. X    }
  1472. X    if (tidy_ignored && (gh->master_flag & M_IGNORE_GROUP)) {
  1473. X        why = "Ignored group:   ";
  1474. X        goto delete;
  1475. X    }
  1476. X    if (tidy_crap && (gh->group_flag & G_FAKED)) {
  1477. X        why = "Crap in .newsrc: ";
  1478. X        goto delete;
  1479. X    }
  1480. X    if (tidy_remove_unsub && (gh->group_flag & G_UNSUBSCRIBED)) {
  1481. X        if (gh->group_flag & G_FAKED) continue;
  1482. X        why = "Unsubscribed:    ";
  1483. X        goto delete;
  1484. X    }
  1485. X
  1486. X    if (tidy_unsubscribed && (gh->group_flag & G_UNSUBSCRIBED)) {
  1487. X        if (gh->group_flag & G_FAKED) continue;
  1488. X
  1489. X        begin_rc_update(gh);
  1490. X        gh->last_db_article = 0;
  1491. X        end_rc_update(gh);
  1492. X
  1493. X        if (gh->newsrc_line != gh->newsrc_orig) {
  1494. X        why = "Truncated:       ";
  1495. X        goto change;
  1496. X        }
  1497. X    }
  1498. X    if (verbose) {
  1499. X        why = "Ok:              ";
  1500. X        goto report;
  1501. X    }
  1502. X    continue;
  1503. X
  1504. X     delete:
  1505. X    gh->newsrc_line = NULL;
  1506. X    gh->select_line = NULL;
  1507. X
  1508. X     change:
  1509. X    changed = 1;
  1510. X
  1511. X     report:
  1512. X    if (!silent) printf("%s%s\n", why, gh->group_name);
  1513. X    }
  1514. X
  1515. X    if (changed) {
  1516. X    newsrc_update_freq = 0;
  1517. X    dump_newsrc();
  1518. X    dump_select();
  1519. X    printf("NOTICE: Original files are saved with %s extention\n", bak_suffix);
  1520. X    }
  1521. X}
  1522. X
  1523. X/*
  1524. X *    nngoback
  1525. X */
  1526. X
  1527. Xstatic int
  1528. X    goback_interact = 0, /* interactive nngoback */
  1529. X    goback_days = -1,
  1530. X    goback_alsounsub = 0; /* unsubscribed groups also */
  1531. X
  1532. XOption_Description(goback_options) {
  1533. X    'N', Bool_Option(no_update),
  1534. X    'Q', Bool_Option(silent),
  1535. X    'd', Int_Option(goback_days),
  1536. X    'i', Bool_Option(goback_interact),
  1537. X    'u', Bool_Option(goback_alsounsub),
  1538. X    'v', Bool_Option(verbose),
  1539. X    '\0',
  1540. X};
  1541. X
  1542. Xopt_nngoback(argc, argvp)
  1543. Xint argc;
  1544. Xchar ***argvp;
  1545. X{
  1546. X    int n;
  1547. X    
  1548. X    n = parse_options(argc, *argvp, (char *)NULL, goback_options,
  1549. X              " days [groups]...");
  1550. X
  1551. X    if (goback_days < 0) {
  1552. X    if (n == 0 || !isdigit((*argvp)[1][0])) {
  1553. X        fprintf(stderr, "usage: %s [-NQvi] days [groups]...\n", pname);
  1554. X        nn_exit(1);
  1555. X    }
  1556. X    goback_days = atoi((*argvp)[1]);
  1557. X    n--;
  1558. X    ++*argvp;
  1559. X    }
  1560. X    return n;
  1561. X}
  1562. X
  1563. Xdo_goback()
  1564. X{
  1565. X    char back_act[FILENAME];
  1566. X    FILE *ba;
  1567. X    register group_header *gh;
  1568. X    int32 count, total;
  1569. X    int groups, y;
  1570. X    
  1571. X    sprintf(back_act, "%s/active.%d", db_directory, goback_days);
  1572. X    if ((ba = open_file(back_act, OPEN_READ)) == NULL) {
  1573. X    fprintf(stderr, "Cannot go back %d days\n", goback_days);
  1574. X    nn_exit(1);
  1575. X    }
  1576. X
  1577. X    read_active_file(ba, (FILE *)NULL);
  1578. X
  1579. X    fclose(ba);
  1580. X
  1581. X    /* visit_rc_file has been called. */
  1582. X
  1583. X    keep_rc_backup = 1;
  1584. X    bak_suffix = ".goback";
  1585. X    newsrc_update_freq = 9999;
  1586. X    quick_unread_count = 0;
  1587. X    total = groups = 0;
  1588. X
  1589. X    if (goback_interact) {
  1590. X    init_term();
  1591. X    raw();
  1592. X    }
  1593. X
  1594. X    Loop_Groups_Sequence(gh) {
  1595. X    if ((gh->master_flag & M_VALID) == 0) continue;
  1596. X    if (!goback_alsounsub && (gh->group_flag & G_UNSUBSCRIBED)) continue;
  1597. X
  1598. X    add_unread(gh, 1);
  1599. X
  1600. X    count = restore_rc(gh, gh->last_a_article);
  1601. X    if (count > 0) {
  1602. X        if (goback_interact) {
  1603. X        printf("%s + %ld ?  (y) ", gh->group_name, (long)count); fl;
  1604. X        y = yes(0);
  1605. X        putchar(CR); putchar(NL);
  1606. X        switch (y) {
  1607. X         case 1:
  1608. X            break;
  1609. X         case 0:
  1610. X            gh->newsrc_line = gh->newsrc_orig;
  1611. X            gh->select_line = gh->select_orig;
  1612. X            continue;
  1613. X         case -1:
  1614. X            if (total > 0) {
  1615. X            printf("\nSave changes sofar? (n) "); fl;
  1616. X            if (yes(1) <= 0) nn_exit(0);
  1617. X            }
  1618. X            goto out;
  1619. X        }
  1620. X        } else
  1621. X        if (verbose)
  1622. X            printf("%5ld\t%s\n", (long)count, gh->group_name);
  1623. X
  1624. X        total += count;
  1625. X        groups++;
  1626. X    }
  1627. X    }
  1628. X
  1629. X out:
  1630. X
  1631. X    if (total == 0) {
  1632. X    printf("No articles marked\n");
  1633. X    return;
  1634. X    }
  1635. X
  1636. X    flush_newsrc();
  1637. X
  1638. X    if (verbose) putchar(NL);
  1639. X    if (!silent)
  1640. X    printf("%ld article%s marked unread in %d group%s\n",
  1641. X           (long)total, plural((long)total),
  1642. X           groups, plural((long)groups));
  1643. X}
  1644. X
  1645. X/* fake this for read_active_file */
  1646. X
  1647. Xgroup_header *add_new_group(name)
  1648. Xchar *name;
  1649. X{
  1650. X    return NULL;
  1651. X}
  1652. END_OF_FILE
  1653.   if test 34671 -ne `wc -c <'newsrc.c'`; then
  1654.     echo shar: \"'newsrc.c'\" unpacked with wrong size!
  1655.   fi
  1656.   # end of 'newsrc.c'
  1657. fi
  1658. if test -f 'sequence.c' -a "${1}" != "-c" ; then 
  1659.   echo shar: Will not clobber existing file \"'sequence.c'\"
  1660. else
  1661.   echo shar: Extracting \"'sequence.c'\" \(14489 characters\)
  1662.   sed "s/^X//" >'sequence.c' <<'END_OF_FILE'
  1663. X/*
  1664. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  1665. X *
  1666. X *    Read presentation sequence file
  1667. X */
  1668. X
  1669. X#include "config.h"
  1670. X#include "debug.h"
  1671. X
  1672. Xexport group_header *group_sequence;
  1673. Xexport char *read_mail = NULL;
  1674. Xexport int also_subgroups = 1;
  1675. Xexport int hex_group_args = 0;
  1676. X
  1677. Xstatic int seq_break_enabled = 1;    /* !! enabled */
  1678. Xstatic int ignore_done_flag = 0;     /* % toggle */
  1679. X
  1680. Xstatic group_header *tail_sequence = NULL;
  1681. Xstatic group_header *final_sequence = NULL;
  1682. X
  1683. Xstatic int gs_more_groups;
  1684. X
  1685. X
  1686. Xonly_folder_args(args)
  1687. Xchar **args;
  1688. X{
  1689. X    register char *arg;
  1690. X
  1691. X    while (arg = *args++) {
  1692. X    if (*arg == '+' || *arg == '~' || *arg == '/') continue;
  1693. X    if (file_exist(arg, "fr")) continue;
  1694. X    return 0;
  1695. X    }
  1696. X    return 1;
  1697. X}
  1698. X
  1699. X
  1700. X#define    SHOW_NORMAL    0    /*   : put this in at current pos */
  1701. X#define    SHOW_FIRST    1    /* < : show these groups first */
  1702. X#define    SHOW_LAST    2    /* > : show this as late as possible */
  1703. X#define    IGNORE_ALWAYS    3    /* ! : ignore these groups completely */
  1704. X#define IGN_UNLESS_RC    4    /* !:X ignore these groups unless in rc */
  1705. X#define    IGN_UNLESS_NEW    5    /* !:O ignore these groups unless new */
  1706. X#define IGN_UNL_RC_NEW    6    /* !:U ignore unsubscribed */
  1707. X#define    IGN_IF_NEW    7    /* !:N ignore these groups if new */
  1708. X
  1709. X#define    SHOW_MODES    " <>!-?*"
  1710. X
  1711. Xstatic enter_sequence(mode, gh)
  1712. Xint mode;
  1713. Xgroup_header *gh;
  1714. X{
  1715. X#ifdef SEQ_TEST
  1716. X    if (Debug & SEQ_TEST && mode != SHOW_NORMAL)
  1717. X    printf("SEQ(%c), %s\n", SHOW_MODES[mode], gh->group_name);
  1718. X#endif
  1719. X
  1720. X    if (gh->master_flag & M_IGNORE_GROUP) return 0;
  1721. X    if (ignore_done_flag) {
  1722. X    if (gh->group_flag & G_SEQUENCE) return 0;
  1723. X    } else
  1724. X    if (gh->group_flag & G_DONE) return 0;
  1725. X
  1726. X    switch (mode) {
  1727. X     case IGN_UNLESS_NEW:
  1728. X    if ((gh->group_flag & G_NEW) == 0)
  1729. X        gh->group_flag |= G_DONE;
  1730. X    return 0;
  1731. X
  1732. X     case IGN_IF_NEW:
  1733. X    if (gh->group_flag & G_NEW)
  1734. X        gh->group_flag |= G_DONE;
  1735. X    return 0;
  1736. X
  1737. X     case IGN_UNL_RC_NEW:
  1738. X    if (gh->group_flag & G_NEW) return 0;
  1739. X        if (gh->newsrc_line == NULL || (gh->group_flag & G_UNSUBSCRIBED))
  1740. X        gh->group_flag |= G_DONE;
  1741. X    return 0;
  1742. X
  1743. X     case IGN_UNLESS_RC:
  1744. X        if (gh->newsrc_line == NULL || (gh->group_flag & (G_UNSUBSCRIBED|G_NEW)))
  1745. X        gh->group_flag |= G_DONE;
  1746. X    return 0;
  1747. X
  1748. X     case IGNORE_ALWAYS:
  1749. X    gh->group_flag |= G_DONE;
  1750. X    return 0;
  1751. X
  1752. X     default:
  1753. X    gh->group_flag |= G_DONE;
  1754. X    break;
  1755. X    }
  1756. X
  1757. X    gh->group_flag |= G_SEQUENCE;
  1758. X
  1759. X    if (gh->master_flag & M_NO_DIRECTORY)
  1760. X    return 0;        /* for nntidy -s */
  1761. X
  1762. X    switch (mode) {
  1763. X     case SHOW_FIRST:
  1764. X    if (tail_sequence) {
  1765. X        gh->next_group = group_sequence;
  1766. X        group_sequence = gh;
  1767. X        break;
  1768. X    }
  1769. X    /* fall thru */
  1770. X
  1771. X     case SHOW_NORMAL:
  1772. X    if (tail_sequence)
  1773. X        tail_sequence->next_group = gh;
  1774. X    else
  1775. X        group_sequence = gh;
  1776. X    tail_sequence = gh;
  1777. X    break;
  1778. X
  1779. X     case SHOW_LAST:
  1780. X    gh->next_group = final_sequence;
  1781. X    final_sequence = gh;
  1782. X    break;
  1783. X    }
  1784. X    return 1;
  1785. X}
  1786. X
  1787. X
  1788. Xstatic faked_entry(name, flag)
  1789. Xchar *name;
  1790. Xflag_type flag;
  1791. X{
  1792. X    group_header *gh;
  1793. X
  1794. X    gh = newobj(group_header, 1);
  1795. X
  1796. X    gh->group_name = name;
  1797. X    gh->group_flag = flag | G_FAKED;
  1798. X    gh->master_flag = 0;
  1799. X
  1800. X    /* "invent" an unread article for read_news */
  1801. X    gh->last_article = 1;
  1802. X    gh->last_db_article = 2;
  1803. X
  1804. X    enter_sequence(SHOW_NORMAL, gh);
  1805. X}
  1806. X
  1807. Xstatic end_sequence()
  1808. X{
  1809. X    register group_header *gh, *backp;
  1810. X    register int seq_ix;
  1811. X
  1812. X    if (tail_sequence)
  1813. X    tail_sequence->next_group = NULL;
  1814. X
  1815. X    /* set up backward pointers */
  1816. X
  1817. X    backp = NULL;
  1818. X    seq_ix = 0;
  1819. X    Loop_Groups_Sequence(gh) {
  1820. X    gh->preseq_index = (gh->group_flag & G_UNSUBSCRIBED) ? 0 : ++seq_ix;
  1821. X    gh->prev_group = backp;
  1822. X    backp = gh;
  1823. X    }
  1824. X
  1825. X#ifdef SEQ_DUMP
  1826. X    if (Debug & SEQ_DUMP) {
  1827. X    for (gh = group_sequence; gh; gh = gh->next_group)
  1828. X        printf("%s\t", gh->group_name);
  1829. X    putchar(NL);
  1830. X
  1831. X    nn_exit(0);
  1832. X    }
  1833. X#endif
  1834. X
  1835. X}
  1836. X
  1837. X
  1838. X#ifdef MAIL_READING
  1839. Xstatic mail_check()
  1840. X{
  1841. X    static group_header mail_group;
  1842. X    struct stat st;
  1843. X
  1844. X    if (read_mail == NULL) return;
  1845. X    if (stat(read_mail, &st) < 0) return;
  1846. X    if (st.st_size == 0 || st.st_mtime < st.st_atime) return;
  1847. X
  1848. X    mail_group.group_name = read_mail;
  1849. X    gh->group_flag = G_FOLDER | G_MAILBOX | G_FAKED;
  1850. X    gh->master_flag = 0;
  1851. X
  1852. X    /* "invent" an unread article for read_news */
  1853. X    gh->last_article = 1;
  1854. X    gh->last_db_article = 2;
  1855. X
  1856. X
  1857. X    if (tail_sequence) {
  1858. X    mail_group.next_group = group_sequence;
  1859. X    group_sequence = mail_group;
  1860. X    } else
  1861. X    enter_sequence(SHOW_NORMAL, &mail_group);
  1862. X}
  1863. X#endif
  1864. X
  1865. X
  1866. X
  1867. Xstatic visit_presentation_file(directory, seqfile, hook)
  1868. Xchar *directory, *seqfile;
  1869. XFILE *hook;
  1870. X{
  1871. X    import int group_name_args;
  1872. X
  1873. X    register FILE *sf;
  1874. X    register c;
  1875. X    register group_header *gh;
  1876. X    group_header *mp_group, *get_group_search();
  1877. X    char group[FILENAME], *gname;
  1878. X    char savefile[FILENAME], *dflt_save, *enter_macro;
  1879. X    extern char *parse_enter_macro();
  1880. X    register char *gp;
  1881. X    int mode, merge_groups;
  1882. X
  1883. X    if (gs_more_groups == 0) return 0;
  1884. X
  1885. X    if (hook != NULL)
  1886. X    sf = hook;    /* hook to init file */
  1887. X    else
  1888. X    if ((sf = open_file(relative(directory, seqfile), OPEN_READ)) == NULL)
  1889. X        return 0;
  1890. X
  1891. X#ifdef SEQ_TEST
  1892. X    if (Debug & SEQ_TEST)
  1893. X    printf("Sequence file %s/%s\n", directory, seqfile);
  1894. X#endif
  1895. X
  1896. X    mode = SHOW_NORMAL;
  1897. X    savefile[0] = NUL;
  1898. X
  1899. X    while (gs_more_groups) {
  1900. X
  1901. X    if ((c = getc(sf)) == EOF) break;
  1902. X    if (!isascii(c) || isspace(c)) continue;
  1903. X
  1904. X    switch (c) {
  1905. X     case '!':
  1906. X        mode = IGNORE_ALWAYS;
  1907. X        if ((c = getc(sf)) == EOF) continue;
  1908. X        if (c == '!') {
  1909. X        if (seq_break_enabled) {
  1910. X            fclose(sf);
  1911. X            return 1;
  1912. X        }
  1913. X        mode = SHOW_NORMAL;
  1914. X        continue;
  1915. X        }
  1916. X        if (c == ':') {
  1917. X        if ((c = getc(sf)) == EOF) continue;
  1918. X        if (!isascii(c) || isspace(c) || !isupper(c)) continue;
  1919. X        switch (c) {
  1920. X         case 'O':
  1921. X            mode = IGN_UNLESS_NEW;
  1922. X            continue;
  1923. X         case 'N':
  1924. X            mode = IGN_IF_NEW;
  1925. X            continue;
  1926. X         case 'U':
  1927. X            mode = IGN_UNL_RC_NEW;
  1928. X            continue;
  1929. X         case 'X':
  1930. X            mode = IGN_UNLESS_RC;
  1931. X            continue;
  1932. X         default:
  1933. X            /*should give error here*/
  1934. X            mode = SHOW_NORMAL;
  1935. X            continue;
  1936. X        }
  1937. X        }
  1938. X        ungetc(c, sf);
  1939. X        continue;
  1940. X
  1941. X     case '<':
  1942. X        mode = SHOW_FIRST;
  1943. X        continue;
  1944. X
  1945. X     case '>':
  1946. X        mode = SHOW_LAST;
  1947. X        continue;
  1948. X
  1949. X     case '%':
  1950. X        ignore_done_flag = ! ignore_done_flag;
  1951. X        continue;
  1952. X
  1953. X     case '@':
  1954. X        seq_break_enabled = 0;
  1955. X        mode = SHOW_NORMAL;
  1956. X        continue;
  1957. X
  1958. X     case '#':
  1959. X        do c = getc(sf);
  1960. X        while (c != EOF && c != NL);
  1961. X        mode = SHOW_NORMAL;
  1962. X        continue;
  1963. X
  1964. X    }
  1965. X
  1966. X    gp = group;
  1967. X    merge_groups = 0;
  1968. X    do {
  1969. X        *gp++ = c;
  1970. X        if (c == ',') merge_groups = 1;
  1971. X        c = getc(sf);
  1972. X    } while (c != EOF && isascii(c) && !isspace(c));
  1973. X
  1974. X    *gp = NUL;
  1975. X
  1976. X    while (c != EOF && (!isascii(c) || isspace(c))) c = getc(sf);
  1977. X    if (c == '+' || c == '~' || c == '/') {
  1978. X        gp = savefile;
  1979. X        if (c == '+') {
  1980. X        c = getc(sf);
  1981. X        if (c == EOF || (isascii(c) && isspace(c)))
  1982. X            goto use_same_savefile;
  1983. X        *gp++ = '+';
  1984. X        }
  1985. X        do {
  1986. X        *gp++ = c;
  1987. X        c = getc(sf);
  1988. X        } while (c != EOF && isascii(c) && !isspace(c));
  1989. X        *gp = NUL;
  1990. X        dflt_save = savefile[0] ? copy_str(savefile) : NULL;
  1991. X    } else
  1992. X        dflt_save = NULL;
  1993. X
  1994. X     use_same_savefile:
  1995. X    while (c != EOF && (!isascii(c) || isspace(c))) c = getc(sf);
  1996. X    if (c == '(') {
  1997. X        enter_macro = parse_enter_macro(sf, getc(sf));
  1998. X    } else {
  1999. X        enter_macro = NULL;
  2000. X        if (c != EOF) ungetc(c, sf);
  2001. X    }
  2002. X
  2003. X    mp_group = NULL;
  2004. X    for (gp = group; *gp;) {
  2005. X        gname = gp;
  2006. X        if (merge_groups) {
  2007. X        while (*gp && *gp != ',') gp++;
  2008. X        if (*gp) *gp++ = NUL;
  2009. X        }
  2010. X        start_group_search(gname);
  2011. X
  2012. X        while (gh = get_group_search()) {
  2013. X        if (!enter_sequence(mode, gh)) continue;
  2014. X
  2015. X        if (merge_groups && (gh->group_flag & G_UNSUBSCRIBED) == 0) {
  2016. X            if (mp_group == NULL) {
  2017. X            gh->group_flag |= G_MERGE_HEAD;
  2018. X            } else {
  2019. X            mp_group->merge_with = gh;
  2020. X            gh->group_flag |= G_MERGE_SUB;
  2021. X            }
  2022. X            mp_group = gh;
  2023. X        }
  2024. X
  2025. X        if (gh->save_file == NULL) /* not set by "save-files" */
  2026. X            gh->save_file = dflt_save;
  2027. X        if (gh->enter_macro == NULL) /* not set by "on entry" */
  2028. X            gh->enter_macro = enter_macro;
  2029. X        }
  2030. X        if (!merge_groups) *gp = NUL;
  2031. X    }
  2032. X    if (merge_groups && mp_group != NULL)
  2033. X        mp_group->merge_with = NULL;
  2034. X    mode = SHOW_NORMAL;
  2035. X    }
  2036. X
  2037. X    fclose(sf);
  2038. X    return 0;
  2039. X}
  2040. X
  2041. Xparse_save_files(sf)
  2042. Xregister FILE *sf;
  2043. X{
  2044. X    register c;
  2045. X    register group_header *gh;
  2046. X    group_header *get_group_search();
  2047. X    char group[FILENAME];
  2048. X    char *savefile = NULL;
  2049. X    char namebuf[FILENAME];
  2050. X    register char *gp;
  2051. X
  2052. X    for (;;) {
  2053. X    if ((c = getc(sf)) == EOF) break;
  2054. X    if (!isascii(c) || isspace(c)) continue;
  2055. X    if (c == '#') {
  2056. X        do c = getc(sf); while (c != EOF && c != NL);
  2057. X        continue;
  2058. X    }
  2059. X    gp = group;
  2060. X    do {
  2061. X        *gp++ = c;
  2062. X        c = getc(sf);
  2063. X    } while (c != EOF && isascii(c) && !isspace(c));
  2064. X    *gp = NUL;
  2065. X
  2066. X    if (strcmp(group, "end") == 0) break;
  2067. X
  2068. X    while (c != EOF && (!isascii(c) || isspace(c))) c = getc(sf);
  2069. X
  2070. X    gp = namebuf;
  2071. X    do {
  2072. X        *gp++ = c;
  2073. X        c = getc(sf);
  2074. X    } while (c != EOF && isascii(c) && !isspace(c));
  2075. X    *gp = NUL;
  2076. X    if (namebuf[0] == NUL) break;
  2077. X    if (strcmp(namebuf, "+"))
  2078. X        savefile = copy_str(namebuf);
  2079. X
  2080. X    start_group_search(group);
  2081. X
  2082. X    while (gh = get_group_search())
  2083. X        gh->save_file = savefile;
  2084. X    }
  2085. X}
  2086. X
  2087. Xnamed_group_sequence(groups)
  2088. Xchar **groups;
  2089. X{
  2090. X    register group_header *gh;
  2091. X    group_header *get_group_search();
  2092. X    register char *group;
  2093. X    int found, any, errors, gnum;
  2094. X
  2095. X    group_sequence = NULL;
  2096. X    also_subgroups = 0;
  2097. X
  2098. X    any = errors = 0;
  2099. X    while (group = *groups++) {
  2100. X
  2101. X    if (hex_group_args) {
  2102. X        sscanf(group, "%x", &gnum);
  2103. X        if (gnum < 0 || gnum >= master.number_of_groups) continue;
  2104. X        gh = &active_groups[gnum];
  2105. X        if (enter_sequence(SHOW_NORMAL, gh)) any++;
  2106. X        continue;
  2107. X    }
  2108. X
  2109. X    if (gh = lookup(group)) {
  2110. X        if (enter_sequence(SHOW_NORMAL, gh)) any++;
  2111. X        continue;
  2112. X    }
  2113. X
  2114. X    if (file_exist(group, "fr")) {
  2115. X        faked_entry(group, G_FOLDER);
  2116. X        any++;
  2117. X        continue;
  2118. X    }
  2119. X
  2120. X    if (*group == '+' || *group == '~') {
  2121. X        char exp_file[FILENAME];
  2122. X        group_header fake_group;
  2123. X
  2124. X        current_group = &fake_group;
  2125. X        fake_group.group_name = group;
  2126. X        group_file_name = NULL;
  2127. X        if (expand_file_name(exp_file, group, 1) && file_exist(exp_file, "fr")) {
  2128. X        faked_entry(copy_str(exp_file), G_FOLDER);
  2129. X        any++;
  2130. X        continue;
  2131. X        }
  2132. X
  2133. X        printf("Folder %s not found\n", group); fl;
  2134. X        errors++;
  2135. X        continue;
  2136. X    }
  2137. X
  2138. X    found = 0;
  2139. X    start_group_search(group);
  2140. X    while (gh = get_group_search()) {
  2141. X        found++;
  2142. X        enter_sequence(SHOW_NORMAL, gh);
  2143. X    }
  2144. X
  2145. X    if (!found) {
  2146. X        printf("Group %s not found\n", group); fl;
  2147. X        errors++;
  2148. X    } else
  2149. X        any++;
  2150. X    }
  2151. X
  2152. X    end_sequence();
  2153. X
  2154. X    if (errors) user_delay(2);
  2155. X
  2156. X    return any;
  2157. X}
  2158. X
  2159. XFILE *loc_seq_hook = NULL;    /* sequence in local "init" file */
  2160. XFILE *glob_seq_hook = NULL;    /* sequence in global "init" file */
  2161. X
  2162. Xnormal_group_sequence()
  2163. X{
  2164. X    register group_header *gh;
  2165. X
  2166. X    group_sequence = NULL;
  2167. X    gs_more_groups = 1;
  2168. X
  2169. X    /* visit_p_f returns non-zero if terminated by !! */
  2170. X
  2171. X    if (visit_presentation_file(nn_directory, "seq", loc_seq_hook))
  2172. X    goto final;
  2173. X
  2174. X    if (visit_presentation_file(lib_directory, "sequence", glob_seq_hook))
  2175. X    goto final;
  2176. X
  2177. X    Loop_Groups_Sorted(gh) {
  2178. X    enter_sequence(SHOW_NORMAL, gh);
  2179. X    }
  2180. X
  2181. X final:
  2182. X    if (final_sequence)
  2183. X    if (tail_sequence) {
  2184. X        tail_sequence->next_group = final_sequence;
  2185. X        tail_sequence = NULL;
  2186. X    } else
  2187. X        group_sequence = final_sequence;
  2188. X
  2189. X#ifdef MAIL_READING
  2190. X    mail_check();
  2191. X#endif
  2192. X
  2193. X    end_sequence();
  2194. X}
  2195. X
  2196. X
  2197. X
  2198. Xstatic char *gs_group;
  2199. Xstatic int gs_length, gs_index, gs_mode;
  2200. Xstatic group_header *gs_only_group = NULL;
  2201. X
  2202. X#define GS_PREFIX0    0    /* group (or group*) */
  2203. X#define    GS_PREFIX    1    /* group. */
  2204. X#define    GS_SUFFIX    2    /* .group */
  2205. X#define GS_INFIX    3    /* .group. */
  2206. X#define GS_NEW_GROUP    4    /* new group */
  2207. X#define GS_ALL        5    /* all / . */
  2208. X#define    GS_NEWSRC    6    /* RC */
  2209. X
  2210. Xstart_group_search(group)
  2211. Xchar *group;
  2212. X{
  2213. X    char *dot;
  2214. X    int last;
  2215. X    import group_header *rc_sequence;
  2216. X
  2217. X    gs_index = master.number_of_groups;    /* loop will fail */
  2218. X
  2219. X    if ((last = strlen(group) - 1) < 0) return;
  2220. X    if (group[last] == '*')
  2221. X    group[last] = NUL;
  2222. X    else
  2223. X    if (!also_subgroups && (gs_only_group = lookup(group)) != NULL)
  2224. X        return;
  2225. X
  2226. X    gs_index = 0;
  2227. X    gs_more_groups = 0;
  2228. X    gs_length = 0;
  2229. X    gs_group = NULL;
  2230. X
  2231. X    if (strcmp(group, "NEW") == 0) {
  2232. X    gs_mode = GS_NEW_GROUP;
  2233. X    return;
  2234. X    }
  2235. X
  2236. X    if (strncmp(group, "RC", 2) == 0) {
  2237. X    gs_mode = GS_NEWSRC;
  2238. X    gs_only_group = rc_sequence;
  2239. X    gs_more_groups = 1;    /* we just can't know! */
  2240. X
  2241. X    if (group[2] != ':') return;
  2242. X    if (isdigit(group[3]))
  2243. X        gs_index = atoi(group+3);
  2244. X    else {
  2245. X        gs_group = group+3;
  2246. X        gs_length = strlen(gs_group);
  2247. X    }
  2248. X    return;
  2249. X    }
  2250. X
  2251. X    if (strcmp(group, "all") == 0 || strcmp(group, ".") == 0) {
  2252. X    gs_mode = GS_ALL;
  2253. X    return;
  2254. X    }
  2255. X
  2256. X    gs_mode = GS_PREFIX0;
  2257. X
  2258. X    if (strncmp(group, "all.", 4) == 0) group += 3;
  2259. X
  2260. X    if (*group == '.') gs_mode = GS_SUFFIX;
  2261. X
  2262. X    if ((dot = strrchr(group, '.')) != NULL && dot != group) {
  2263. X    if (dot[1] == NUL || strcmp(dot+1, "all") == 0) {
  2264. X        dot[1] = NUL;
  2265. X        gs_mode = (gs_mode == GS_SUFFIX) ? GS_INFIX : GS_PREFIX;
  2266. X    }
  2267. X    }
  2268. X
  2269. X    gs_length = strlen(group);
  2270. X    gs_group = group;
  2271. X}
  2272. X
  2273. Xgroup_header *get_group_search()
  2274. X{
  2275. X    register group_header *gh;
  2276. X    register int c, tail;
  2277. X
  2278. X    if (gs_mode == GS_NEWSRC) {
  2279. X    do {
  2280. X        gh = gs_only_group;
  2281. X        if (gh == NULL) return NULL;
  2282. X        if (gs_index && --gs_index == 0) {
  2283. X        gs_only_group = NULL;
  2284. X        } else
  2285. X        if (gs_group && gh->group_name_length >= gs_length &&
  2286. X        strncmp(gh->group_name, gs_group, gs_length) == 0) {
  2287. X        gs_only_group = NULL;
  2288. X        } else
  2289. X        gs_only_group = gh->newsrc_seq;
  2290. X    } while ((!ignore_done_flag && (gh->group_flag & G_DONE)) ||
  2291. X         (gh->master_flag & M_IGNORE_GROUP));
  2292. X    return gh;
  2293. X    }
  2294. X
  2295. X    if (gs_only_group != NULL) {
  2296. X    gh = gs_only_group;
  2297. X    gs_only_group = NULL;
  2298. X    if (!ignore_done_flag && gh->group_flag & G_DONE) return NULL;
  2299. X    if (gh->master_flag & M_IGNORE_GROUP) return NULL;
  2300. X    return gh;
  2301. X    }
  2302. X
  2303. X    while (gs_index < master.number_of_groups) {
  2304. X    gh = sorted_groups[gs_index++];
  2305. X    if (!ignore_done_flag && gh->group_flag & G_DONE) continue;
  2306. X    if (gh->master_flag & M_IGNORE_GROUP) continue;
  2307. X
  2308. X    gs_more_groups++;
  2309. X
  2310. X    if ((tail = gh->group_name_length - gs_length) < 0) continue;
  2311. X
  2312. X    switch (gs_mode) {
  2313. X
  2314. X     case GS_NEW_GROUP:
  2315. X        if ((gh->group_flag & G_NEW) == 0) continue;
  2316. X        break;
  2317. X
  2318. X     case GS_PREFIX0:
  2319. X        if ((c = (gh->group_name)[gs_length]) != NUL && c != '.') continue;
  2320. X     case GS_PREFIX:
  2321. X        if (strncmp(gh->group_name, gs_group, gs_length)) continue;
  2322. X        break;
  2323. X
  2324. X     case GS_SUFFIX:
  2325. X        if (strcmp(gh->group_name + tail, gs_group)) continue;
  2326. X        break;
  2327. X
  2328. X     case GS_INFIX:
  2329. X        user_error(".name. notation not supported (yet)");
  2330. X        break;
  2331. X
  2332. X     case GS_ALL:
  2333. X        break;
  2334. X    }
  2335. X
  2336. X    gs_more_groups--;
  2337. X    return gh;
  2338. X    }
  2339. X
  2340. X    return NULL;
  2341. X}
  2342. END_OF_FILE
  2343.   if test 14489 -ne `wc -c <'sequence.c'`; then
  2344.     echo shar: \"'sequence.c'\" unpacked with wrong size!
  2345.   fi
  2346.   # end of 'sequence.c'
  2347. fi
  2348. echo shar: End of archive 5 \(of 22\).
  2349. cp /dev/null ark5isdone
  2350. MISSING=""
  2351. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do
  2352.     if test ! -f ark${I}isdone ; then
  2353.     MISSING="${MISSING} ${I}"
  2354.     fi
  2355. done
  2356. if test "${MISSING}" = "" ; then
  2357.     echo You have unpacked all 22 archives.
  2358.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2359. else
  2360.     echo You still must unpack the following archives:
  2361.     echo "        " ${MISSING}
  2362. fi
  2363. exit 0
  2364.  
  2365. exit 0 # Just in case...
  2366.